Skip to content

feat(perps-controller): centralize market category classification#9009

Open
abretonc7s wants to merge 6 commits into
mainfrom
feat/perps-centralize-market-category-filter
Open

feat(perps-controller): centralize market category classification#9009
abretonc7s wants to merge 6 commits into
mainfrom
feat/perps-centralize-market-category-filter

Conversation

@abretonc7s
Copy link
Copy Markdown
Contributor

@abretonc7s abretonc7s commented Jun 4, 2026

Explanation

MetaMask Mobile currently re-derives "which market category does this market belong to" in the client. Two in-flight features need the same mapping and each implemented it independently:

Both lean on a mobile-local isEquityAsset plus a hand-maintained stock/commodity/forex/crypto switch. Keeping this in the clients means every new MarketCategory (the controller already grew stock/pre-ipo/index/etf in 7.0.0) forces a parallel edit in each client, and the mappings drift.

This PR centralises the classification in @metamask/perps-controller so consumers share one source of truth:

  • STOCK_LIKE_MARKET_TYPES — the set of stock-like categories (stock, pre-ipo, index, etf) that share the stocks filter and traditional market hours.
  • isEquityAsset(marketType) — predicate over that set (moved out of the mobile client).
  • getMarketTypeFilter(market) — maps a PerpsMarketData onto a UI MarketTypeFilter pill. Stock-like → stocks; commoditycommodities; forexforex; any HIP-3 signal (isHip3 / isNewMarket / marketSource) on an otherwise-uncategorized market → all (the crypto pill only contains main-DEX markets); otherwise → crypto.

Clients will replace their local copies with imports in follow-up PRs.

References

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

Note

Medium Risk
Changes how markets map to UI category filters (HIP-3/marketSource and new vs crypto), which can shift list filtering and shortcuts once clients adopt the exports; scope is additive with moved helpers rather than trading or auth paths.

Overview
Centralizes perps market category classification in @metamask/perps-controller so mobile and other clients can import one model instead of duplicating switches.

matchesCategory and applyMarketFilters move from MarketDataService into marketUtils; the service now imports applyMarketFilters only. Both helpers are re-exported from the package root alongside new APIs: getMarketTypeFilter (UI bucket, collapsing stock/pre-ipo/index/etf into stocks), isEquityAsset, isHip3Market, STOCK_LIKE_MARKET_TYPES, and MARKET_TYPE_FILTER named constants.

Classification logic is tightened: isHip3Market treats marketSource as HIP-3 when isHip3 is missing (route-param markets). matchesCategory / getMarketTypeFilter stay aligned on that signal; new matches flagged new markets and uncategorized HIP-3 markets without a marketType. Granular filters (e.g. etfs) stay separate from the collapsed stocks bucket used by getMarketTypeFilter.

Adds unit tests for the new classification helpers in marketUtils.test.ts and documents the release in the changelog.

Reviewed by Cursor Bugbot for commit 998f88e. Bugbot is set up for automated code reviews on this repo. Configure here.

Add getMarketTypeFilter, isEquityAsset, and STOCK_LIKE_MARKET_TYPES so
consumers share a single mapping from the MarketCategory data model to the
UI MarketTypeFilter pills, instead of re-deriving the logic per client and
drifting as new categories are added.
…tegory

Reuse the existing matchesCategory category model instead of a parallel
stock-like grouping. Move matchesCategory and applyMarketFilters from
MarketDataService into marketUtils (pure helpers), add getMarketTypeFilter as
the inverse of matchesCategory, and export all three. Drops the duplicate
STOCK_LIKE_MARKET_TYPES / isEquityAsset added earlier.
@abretonc7s abretonc7s marked this pull request as ready for review June 4, 2026 23:16
@abretonc7s abretonc7s requested review from a team as code owners June 4, 2026 23:16
- Collapse the stock-like categories into the 'stocks' bucket via a single
  isEquityAsset / STOCK_LIKE_MARKET_TYPES source.
- Drive matchesCategory off an exhaustive MARKET_CATEGORY_TO_FILTER map instead
  of a per-category switch; keep only the genuine special cases (all, new,
  crypto's HIP-3 rule).
- getMarketTypeFilter now returns the real user-facing bucket
  (crypto/stocks/commodities/forex/new) and never the 'all' sentinel.
- Add MARKET_TYPE_FILTER named constants for the filter values.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1becc7b. Configure here.

Comment thread packages/perps-controller/src/utils/marketUtils.ts
getMarketTypeFilter treated a marketSource-only market (isHip3 unset, no
marketType) as 'new', but matchesCategory still matched it as 'crypto'. Add a
shared isHip3Market predicate (isHip3 OR marketSource) used by both, and treat
an uncategorized HIP-3 market as 'new' in matchesCategory, so the two classify
partial markets the same way. Equivalent for well-formed data.
Copy link
Copy Markdown
Contributor

@aganglada aganglada left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we think we are exposing too many constants to the clients where we are better exposing methods that expose functionality (ie: filter markets). Constants can change names and structure. Methods expose a functionality that can change overtime, no matter what structure gets handled inside

Comment on lines +87 to +92
export const STOCK_LIKE_MARKET_TYPES: ReadonlySet<MarketType> = new Set([
MarketCategory.Stock,
MarketCategory.PreIpo,
MarketCategory.Index,
MarketCategory.Etf,
]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouln't have have this anymore, we are showing all categories now

Comment on lines +100 to +102
export const isEquityAsset = (marketType?: string): boolean =>
marketType !== undefined &&
STOCK_LIKE_MARKET_TYPES.has(marketType as MarketType);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same we this, we shouldn't need this

Comment on lines +23 to +31
const MARKET_CATEGORY_TO_FILTER: Record<MarketCategory, MarketTypeFilter> = {
[MarketCategory.CryptoCurrency]: MARKET_TYPE_FILTER.Crypto,
[MarketCategory.Stock]: MARKET_TYPE_FILTER.Stocks,
[MarketCategory.PreIpo]: MARKET_TYPE_FILTER.PreIpo,
[MarketCategory.Index]: MARKET_TYPE_FILTER.Indices,
[MarketCategory.Etf]: MARKET_TYPE_FILTER.Etfs,
[MarketCategory.Commodity]: MARKET_TYPE_FILTER.Commodities,
[MarketCategory.Forex]: MARKET_TYPE_FILTER.Forex,
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should align market category with filter category to be the same all singular. The current approach is a bit confusing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants